#ifndef _DATA_TRANS_MANAGER_H_
#define _DATA_TRANS_MANAGER_H_
#include <map>
#include "amdspl.h"
#include "CommonDefs.h"
#include "ProgramInfo.h"
#include "BufferManager.h"
#include "Buffer.h"
#include "LocalBuffer.h"
#include "ConstBuffer.h"
#include "ProgramManager.h"
#include "Program.h"
#include "ComputeProgram.h"
#include "GeneralProgram.h"
#include "Device.h"
#include "VectorDataType.h"

using namespace amdspl;
using namespace amdspl::core::cal;

#define INPUT_NUM 2
#define OUTPUT_NUM 2

#define MAX_REMOTE_BUFFER_LINE_FLOAT4 8192
#define MAX_LOCAL_BUFFER_LINE_FLOAT4 8192
#define MAX_LOCAL_BUFFER_FLOAT_SIZE 8192*8192*4


enum GPU_STATUS { GPU_IDEL=0x0, GPU_INPUT, GPU_COMPUTE, GPU_OUTPUT}; //TODO not used



typedef struct GPU_data_info {
    GPU_STATUS status;

    ConstBuffer *constBufferHandle;

    unsigned int inputBufferNum;
    Buffer **inputBufferHandles;
    
    unsigned int local_data_width;
    unsigned int local_data_height;
    unsigned int localBufferNum;
    LocalBuffer **localBufferHandles;


    unsigned int outputBufferNum;
    Buffer **outputBufferHandles;
    
    Program* transferIN_Program;
    Program* transferOUT_Program;
    Event *transferIN_Event;
    Event *transferOUT_Event;

} GPUComputingTransaction;


class DataTransManager {

public:
    DataTransManager(BufferManager *bufMgr, ProgramManager *progMgr);
    ~DataTransManager(void) {}

    bool beginGPUCompute(Device *device);

    bool initGPUMemory(Device *device, const unsigned int width, const unsigned int height);//not used

    bool transferToGPU_PS(Device *device, float *data, const unsigned int width, const unsigned int height);
    bool transferToGPU_CS(Device *device, float *data, const unsigned int width, const unsigned int height);

    ConstBuffer *getGPUConstBuffer(Device *device);
    LocalBuffer *getGPUGlobalDataBuffer(Device *device);

    bool transferFromGPU_PS(Device *device,float *userMem);
    bool transferFromGPU_CS(Device *device,float *userMem);

    bool endGPUCompute(Device *device);

private:
    BufferManager *_bufMgr;
    ProgramManager *_progMgr;
    ProgramInfo _fft_ps_input_ProgInfo;
    ProgramInfo _fft_cs_input_ProgInfo;
    
    ProgramInfo _fft_ps_ouput_ProgInfo;
    ProgramInfo _fft_cs_ouput_ProgInfo;
    
    map<Device *, GPUComputingTransaction *> GPUDataMap;
    
    inline void destroy(GPUComputingTransaction *current_transaction) {
        if(current_transaction->inputBufferHandles)
            destroy_input_buffer(current_transaction);
        
        destroy_local_buffer(current_transaction);

        if(current_transaction->outputBufferHandles)
            destroy_output_buffer(current_transaction);
        
        delete current_transaction->inputBufferHandles;
        delete current_transaction->outputBufferHandles;
        delete current_transaction;
    }

    inline void destroy_input_buffer(GPUComputingTransaction *current_transaction) {
        for(unsigned int i=0; i<current_transaction->inputBufferNum;i++) {
            if(!current_transaction->inputBufferHandles[i]) continue;
            _bufMgr->destroyBuffer(current_transaction->inputBufferHandles[i]);
            current_transaction->inputBufferHandles[i] = NULL;
        }
    }

    inline void destroy_local_buffer(GPUComputingTransaction *current_transaction) {
        if(current_transaction->localBufferHandles) {
            for(unsigned int i=0; i<current_transaction->localBufferNum;i++) {
                if(!current_transaction->localBufferHandles[i]) continue;
                 _bufMgr->destroyBuffer(current_transaction->localBufferHandles[i]);
                 current_transaction->localBufferHandles[i] = NULL;
            }
        }
        if(current_transaction->constBufferHandle) {
            _bufMgr->destroyBuffer(current_transaction->constBufferHandle );
            current_transaction->constBufferHandle = NULL;
        }
    }

    inline void destroy_output_buffer(GPUComputingTransaction *current_transaction) {
        for(unsigned int i=0; i<current_transaction->outputBufferNum;i++) {
            if(!current_transaction->outputBufferHandles[i]) continue;
            _bufMgr->destroyBuffer(current_transaction->outputBufferHandles[i]);
            current_transaction->outputBufferHandles[i] = NULL;
        }
    }
    
};

typedef union {
	float fv;
	unsigned int uv;
	int iv;
} number;

inline static void debug_info(float *data) {
    int i;
    
    for(i=0;i<4096*8;i+=4) {
        //number num;
        //num.fv = data[i];
        printf("%f, %f, %f, %f\n",data[i], data[i+1], data[i+2], data[i+3]);
        //printf("%d, %f, %f, %f\n",num.iv, data[i+1], data[i+2], data[i+3]);
    }

    /*
    int j;
    for(i=45;i<90;i+=1) {
        for(j=0;j<512;j+=1) {
            int idx = i*512 + j;
            printf("%f, ",data[idx]);
        }
        printf("\n");
    }
    */
    printf("\n");
    printf("\n");
}

#endif
